package com.lboyang.core.proxy;

import com.lboyang.common.enums.RpcErrorMessageEnum;
import com.lboyang.common.enums.RpcResponseCodeEnum;
import com.lboyang.common.exception.RpcException;
import com.lboyang.provider.config.RpcServiceConfig;
import com.lboyang.common.dto.NrpcRequest;
import com.lboyang.common.dto.NrpcResponse;
import com.lboyang.remoting.transport.NrpcRequestTransport;
import com.lboyang.remoting.transport.netty.client.NettyRpcClient;
import lombok.SneakyThrows;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * @Description
 *      客户端代理对象
 * @Date 2021/9/12 16:50
 * @author: A.iguodala
 */
@Slf4j
public class RpcClientProxy implements InvocationHandler {

    private static final String INTERFACE_NAME = "interfaceName";

    /**
     * 将请求传输给服务端
     */
    private final NrpcRequestTransport rpcRequestTransport;

    /**
     * RPC 服务具体信息
     */
    private final RpcServiceConfig rpcServiceConfig;

    public RpcClientProxy(NrpcRequestTransport rpcRequestTransport, RpcServiceConfig rpcServiceConfig) {
        this.rpcRequestTransport = rpcRequestTransport;
        this.rpcServiceConfig = rpcServiceConfig;
    }


    public RpcClientProxy(NrpcRequestTransport rpcRequestTransport) {
        this.rpcRequestTransport = rpcRequestTransport;
        this.rpcServiceConfig = new RpcServiceConfig();
    }

    /**
     * 获取代理对象
     * @param clazz
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    /**
     * 真实调用远程方法
     * @param proxy
     * @param method
     * @param args
     * @return
     */
    @SneakyThrows
    @SuppressWarnings("unchecked")
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        if ("toString".equals(method.getName())) {
            return null;
        }
        log.info("invoked method: [{}]", method.getName());
        NrpcRequest rpcRequest = NrpcRequest.builder().methodName(method.getName())
                .parameters(args)
                .interfaceName(method.getDeclaringClass().getName())
                .paramTypes(method.getParameterTypes())
                .requestId(UUID.randomUUID().toString())
                .group(rpcServiceConfig.getGroup())
                .version(rpcServiceConfig.getVersion())
                .build();
        NrpcResponse<Object> rpcResponse = null;
        if (rpcRequestTransport instanceof NettyRpcClient) {
            CompletableFuture<NrpcResponse<Object>> completableFuture =
                    (CompletableFuture<NrpcResponse<Object>>) rpcRequestTransport.sendRpcRequest(rpcRequest);
            rpcResponse = completableFuture.get();
            System.out.println(rpcResponse);
        }
        this.check(rpcResponse, rpcRequest);
        return rpcResponse.getData();
    }

    /**
     * 检查校验
     * @param rpcResponse
     * @param rpcRequest
     */
    private void check(NrpcResponse<Object> rpcResponse, NrpcRequest rpcRequest) {
        if (rpcResponse == null) {
            throw new RpcException(RpcErrorMessageEnum.SERVICE_INVOCATION_FAILURE, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName());
        }

        if (!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) {
            throw new RpcException(RpcErrorMessageEnum.REQUEST_NOT_MATCH_RESPONSE, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName());
        }

        if (rpcResponse.getCode() == null || !rpcResponse.getCode().equals(RpcResponseCodeEnum.SUCCESS.getCode())) {
            throw new RpcException(RpcErrorMessageEnum.SERVICE_INVOCATION_FAILURE, INTERFACE_NAME + ":" + rpcRequest.getInterfaceName());
        }
    }
}
